﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenTK;

namespace ArtemisTest
{
    //Events :
    //  OnCollision - When a dynamic component collides with differing bounds

    //Methods :
    //  GetBounceBack - returns a normalised vector describing the normal of the collision
    //  MoveDynamics - modifies position data of UID's with dynamics to resolve collisions.
    //  PreUpdate - calls MoveDynamics

    public class PhysicsSystem : CSystem
    {
        Vector2 GetBounceBack(AABB target, AABB stati)
        {
            float dx = target._center._x - stati._center._x;
            float dy = target._center._y - stati._center._y;
            Vector2 v = new Vector2(dx, dy);
            v.NormalizeFast();
            return v;
        }

        public void MoveEntity(long uid, double dx, double dy, double dt)
        {
            Position p = EntitySystem.Components.GetComponent(ComponentType.position, uid) as Position;
            if (Exists(p))
            {
                MoveEntity(p, dx, dy, dt);
            }
        }
        public void MoveEntity(Position p, double dx, double dy, double dt)
        {
            const float VELOCITY_FACTOR = 222f;
            p.X += (float)(dx * dt * VELOCITY_FACTOR);
            p.Y += (float)(dy * dt * VELOCITY_FACTOR);
        }

        void MoveDynamics(double dt)
        {
            const float SIZE_FACTOR = 3f;
            
            Dictionary<long, Component> dynamics = this.EntitySystem.Components.GetAllComponentsOfType(ComponentType.dynamic);
            Dictionary<long, Component> bounds = this.EntitySystem.Components.GetAllComponentsOfType(ComponentType.bounds);
            Dictionary<long, Component> positions = this.EntitySystem.Components.GetAllComponentsOfType(ComponentType.position);

            foreach (KeyValuePair<long, Component> entry in dynamics)
            {
                Bounds b = bounds[entry.Key] as Bounds;
                Position pos = positions[entry.Key] as Position;
                AABB collisionArea = new AABB();
                List<QT<bool>.QTData> collidingEntitys = new List<QT<bool>.QTData>();
                if (Exists(b) && Exists(pos))
                {
                    collisionArea = new AABB(pos.X, pos.Y, b.Width, b.Height);
                    collisionArea.GrowByFactor(SIZE_FACTOR);
                    collidingEntitys = EntitySystem.GetEntitysInBounds(collisionArea, b.UID);
                }
                foreach (QT<bool>.QTData qt in collidingEntitys)
                {
                    Position np = positions[qt.uid] as Position;
                    Bounds nb = bounds[qt.uid] as Bounds;
                    AABB collisionAABB = new AABB(np.X, np.Y, nb.Width, nb.Height);
                    AABB selfAABB = new AABB(pos.X, pos.Y, b.Width, b.Height);
                    if (collisionAABB.IntersectsWithTolerance(selfAABB, 0.2f))
                    {
                        bool force = (!dynamics.ContainsKey(qt.uid));

                        Vector2 v = GetBounceBack(selfAABB, collisionAABB);

                        MoveEntity(pos,v.X, v.Y, dt);

                        //pos.X += (float)(v.X * VELOCITY_FACTOR * dt);
                        //pos.Y += (float)(v.Y * VELOCITY_FACTOR * dt);

                        if (dynamics.ContainsKey(qt.uid))
                            if (dynamics[qt.uid] != null)
                            {
                                Dynamic d = entry.Value as Dynamic;
                                SendSignal(new ScriptEvent() 
                                {
                                    Tag = ScriptEvent.GetTag(ScriptEventId.OnCollision),
                                    SenderUID = d.UID,
                                    OtherUID = qt.uid,
                                    Dt = dt
                                });
                            }

                        while (force)
                        {
                            selfAABB = new AABB(pos.X, pos.Y, b.Width, b.Height);
                            if (!collisionAABB.IntersectsWithTolerance(selfAABB, 0.2f)) { force = false; }
                            v = GetBounceBack(selfAABB, collisionAABB);

                            MoveEntity(pos, v.X, v.Y, dt);
                            //pos.X += (float)(v.X * VELOCITY_FACTOR * dt);
                            //pos.Y += (float)(v.Y * VELOCITY_FACTOR * dt);
                        }
                    }
                }



            }
        }

        public override void Init()
        {
            throw new NotImplementedException();
        }

        public override void PreUpdate(double dt)
        {
            MoveDynamics(dt);
        }

        public override void Update(double dt)
        {
            throw new NotImplementedException();
        }

        public override void PostUpdate(double dt)
        {
            throw new NotImplementedException();
        }

        public PhysicsSystem(EntitySystem es, ScriptSystem ss) : base(es, ss) { }
    }
}
